﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.SqlClient;
using Entity;
using System.Configuration;

namespace Business
{
    public class RecommendationEngineBusiness
    {
        private string connectionString = ConfigurationManager.ConnectionStrings["ServerName"].ToString();

        public List<ProductsEntity> GetProductRecommendations(string username)
        {
            // 获取用户购买历史
            var userPurchaseHistory = GetUserPurchaseHistory(username);

            // 获取所有用户的购买历史
            var allUsersPurchaseHistory = GetAllUsersPurchaseHistory();

            // 计算用户之间的相似度
            var userSimilarities = CalculateUserSimilarities(userPurchaseHistory, allUsersPurchaseHistory);

            // 获取推荐产品
            var recommendedProducts = GetRecommendedProducts(userPurchaseHistory, userSimilarities);

            // 如果推荐产品数量较少，添加一些热门产品作为补充
            if (recommendedProducts.Count < 5) // 假设我们希望至少有5个推荐产品
            {
                var popularProducts = GetPopularProducts();
                var additionalProducts = popularProducts
                    .Where(p => !recommendedProducts.Any(rp => rp.Productname == p.Productname))
                    .Take(5 - recommendedProducts.Count)
                    .ToList();
                recommendedProducts.AddRange(additionalProducts);
            }

            return recommendedProducts;
        }

        private List<OrderdetailsEntity> GetUserPurchaseHistory(string username)
        {
            var purchaseHistory = new List<OrderdetailsEntity>();

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                var command = new SqlCommand("SELECT * FROM orderdetails WHERE username = @username", connection);
                command.Parameters.AddWithValue("@username", username);

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var orderDetail = new OrderdetailsEntity
                        {
                            Orderdetailid = reader.GetInt32(reader.GetOrdinal("orderdetailid")),
                            Orderid = reader.GetInt32(reader.GetOrdinal("orderid")),
                            Username = reader.GetString(reader.GetOrdinal("username")),
                            Productname = reader.GetString(reader.GetOrdinal("productname")),
                            Quantity = reader.GetInt32(reader.GetOrdinal("quantity")),
                            Amount = reader.GetDecimal(reader.GetOrdinal("amount"))
                        };
                        purchaseHistory.Add(orderDetail);
                    }
                }
            }

            return purchaseHistory;
        }

        private Dictionary<string, List<OrderdetailsEntity>> GetAllUsersPurchaseHistory()
        {
            var allUsersPurchaseHistory = new Dictionary<string, List<OrderdetailsEntity>>();

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                var command = new SqlCommand("SELECT * FROM orderdetails", connection);

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var username = reader.GetString(reader.GetOrdinal("username"));
                        var orderDetail = new OrderdetailsEntity
                        {
                            Orderdetailid = reader.GetInt32(reader.GetOrdinal("orderdetailid")),
                            Orderid = reader.GetInt32(reader.GetOrdinal("orderid")),
                            Username = reader.GetString(reader.GetOrdinal("username")),
                            Productname = reader.GetString(reader.GetOrdinal("productname")),
                            Quantity = reader.GetInt32(reader.GetOrdinal("quantity")),
                            Amount = reader.GetDecimal(reader.GetOrdinal("amount"))
                        };

                        if (!allUsersPurchaseHistory.ContainsKey(username))
                        {
                            allUsersPurchaseHistory[username] = new List<OrderdetailsEntity>();
                        }
                        allUsersPurchaseHistory[username].Add(orderDetail);
                    }
                }
            }

            return allUsersPurchaseHistory;
        }

        private Dictionary<string, double> CalculateUserSimilarities(List<OrderdetailsEntity> userPurchaseHistory, Dictionary<string, List<OrderdetailsEntity>> allUsersPurchaseHistory)
        {
            var userSimilarities = new Dictionary<string, double>();

            foreach (var otherUser in allUsersPurchaseHistory)
            {
                if (otherUser.Key != userPurchaseHistory[0].Username)
                {
                    var similarity = CalculateSimilarity(userPurchaseHistory, otherUser.Value);
                    userSimilarities[otherUser.Key] = similarity;
                }
            }

            return userSimilarities;
        }

        private double CalculateSimilarity(List<OrderdetailsEntity> userPurchaseHistory, List<OrderdetailsEntity> otherUserPurchaseHistory)
        {
            var commonProducts = userPurchaseHistory
                .Select(p => p.Productname)
                .Intersect(otherUserPurchaseHistory.Select(p => p.Productname))
                .ToList();

            if (commonProducts.Count == 0)
            {
                return 0;
            }

            var userProductQuantities = userPurchaseHistory
                .GroupBy(p => p.Productname)
                .ToDictionary(g => g.Key, g => g.Sum(p => p.Quantity));

            var otherUserProductQuantities = otherUserPurchaseHistory
                .GroupBy(p => p.Productname)
                .ToDictionary(g => g.Key, g => g.Sum(p => p.Quantity));

            double dotProduct = 0;
            double userMagnitude = 0;
            double otherUserMagnitude = 0;

            foreach (var product in commonProducts)
            {
                dotProduct += userProductQuantities[product] * otherUserProductQuantities[product];
                userMagnitude += Math.Pow(userProductQuantities[product], 2);
                otherUserMagnitude += Math.Pow(otherUserProductQuantities[product], 2);
            }

            userMagnitude = Math.Sqrt(userMagnitude);
            otherUserMagnitude = Math.Sqrt(otherUserMagnitude);

            return dotProduct / (userMagnitude * otherUserMagnitude);
        }

        private List<ProductsEntity> GetRecommendedProducts(List<OrderdetailsEntity> userPurchaseHistory, Dictionary<string, double> userSimilarities)
        {
            var recommendedProductScores = new Dictionary<string, double>();

            foreach (var otherUser in userSimilarities)
            {
                var otherUserPurchaseHistory = GetUserPurchaseHistory(otherUser.Key);

                foreach (var orderDetail in otherUserPurchaseHistory)
                {
                    if (!userPurchaseHistory.Any(p => p.Productname == orderDetail.Productname))
                    {
                        if (!recommendedProductScores.ContainsKey(orderDetail.Productname))
                        {
                            recommendedProductScores[orderDetail.Productname] = 0;
                        }
                        recommendedProductScores[orderDetail.Productname] += otherUser.Value * orderDetail.Quantity;
                    }
                }
            }

            var recommendedProducts = recommendedProductScores
                .OrderByDescending(p => p.Value)
                .Select(p => GetProductDetails(p.Key))
                .ToList();

            return recommendedProducts;
        }

        private ProductsEntity GetProductDetails(string productName)
        {
            ProductsEntity product = null;

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                var command = new SqlCommand("SELECT * FROM products WHERE productname = @productName", connection);
                command.Parameters.AddWithValue("@productName", productName);

                using (var reader = command.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        product = new ProductsEntity
                        {
                            Productid = reader.GetInt32(reader.GetOrdinal("productid")),
                            Productname = reader.GetString(reader.GetOrdinal("productname")),
                            Description = reader.GetString(reader.GetOrdinal("description")),
                            Price = reader.GetDecimal(reader.GetOrdinal("price")),
                            Stockquantity = reader.GetInt32(reader.GetOrdinal("stockquantity")),
                            Salesquantity = reader.GetInt32(reader.GetOrdinal("salesquantity")),
                            Image = reader.GetString(reader.GetOrdinal("image"))
                        };
                    }
                }
            }

            return product;
        }

        private List<ProductsEntity> GetPopularProducts()
        {
            var popularProducts = new List<ProductsEntity>();

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                var command = new SqlCommand("SELECT TOP 5 * FROM products ORDER BY salesquantity DESC", connection);

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var product = new ProductsEntity
                        {
                            Productid = reader.GetInt32(reader.GetOrdinal("productid")),
                            Productname = reader.GetString(reader.GetOrdinal("productname")),
                            Description = reader.GetString(reader.GetOrdinal("description")),
                            Price = reader.GetDecimal(reader.GetOrdinal("price")),
                            Stockquantity = reader.GetInt32(reader.GetOrdinal("stockquantity")),
                            Salesquantity = reader.GetInt32(reader.GetOrdinal("salesquantity")),
                            Image = reader.GetString(reader.GetOrdinal("image"))
                        };
                        popularProducts.Add(product);
                    }
                }
            }

            return popularProducts;
        }
    }
}